﻿using ATEC.ATDataCoreNET;
using ATEC.ATDataCoreNET.ATObjects;
using SSDPNET.ATObjects;
using System.Security.AccessControl;
using System.Windows.Forms;

namespace CmdExample
{
    /// <summary>
    /// 功能：绘制连续线段的命令工具
    /// 描述：此工具支持用户通过鼠标点击输入点，动态生成直线段并实时预览，
    ///      支持右键结束命令或设置为一次性模式生成单条线段。
    /// 类说明：
    /// - 继承自 `ATBasicCmd`，实现了一个绘制线段的命令工具。
    /// - 鼠标交互：支持左键选择点绘制线段，右键结束绘制，鼠标移动实时预览线段。
    /// - 动态特性：实时捕捉鼠标位置，生成临时线段并更新场景显示。
    /// 关键类及功能：
    /// - **XPoint3d**: 表示三维空间中的点，用于定义线段的起点和终点。
    /// - **BasicCurve**: 提供创建基本几何曲线的方法，包括直线段。
    /// - **ATObject**: 表示图形平台中的几何对象，可以添加到场景中。
    /// - **RedrawObjs**: 用于动态绘制对象并实时更新视图。
    /// 注意事项：
    /// - 使用 Snap 精确捕捉用户输入点。
    /// - 连续绘制线段时，上一段的终点会作为下一段的起点。
    /// - 支持动态模式下的实时更新和用户提示。
    /// </summary>
    public class XCmd_CreateLine : ATEC.ATDataCoreNET.ATBasicCmd
    {
        // 保存用户输入的点集合
        System.Collections.Generic.List<ATEC.GeometryNET.XPoint3d> m_points = new System.Collections.Generic.List<ATEC.GeometryNET.XPoint3d>();
        string m_CmdName = ""; // 命令名称
        string m_CmdPrompt = ""; // 命令提示信息

        /// <summary>
        /// 构造函数：初始化命令名称和提示
        /// </summary>
        /// <param name="toolName">工具名称</param>
        /// <param name="toolPrompt">工具提示信息</param>
        XCmd_CreateLine(string toolName, string toolPrompt) : base(toolName, toolPrompt)
        {
            m_CmdName = toolName; // 保存命令名称
            m_CmdPrompt = toolPrompt; // 保存提示信息
        }

        /// <summary>
        /// 命令启动后执行初始化操作
        /// </summary>
        protected override void OnPostInstall()
        {
            ATEC.ATDataCoreNET.ObjectEdit.SnapEnabled = true; // 开启精确捕捉
            base.OnPostInstall(); // 调用父类初始化
        }

        /// <summary>
        /// 鼠标左键事件处理：添加点并绘制线段
        /// </summary>
        /// <param name="ev">鼠标事件参数</param>
        protected override bool OnDataButton(ATEC.ATDataCoreNET.ATBtnEvent ev)
        {
            if (0 == m_points.Count)
                StartRTUpdate(); // 第一次点击时启动动态效果

            m_points.Add(ev.Point); // 保存当前点击位置点
            SetupAndPromptForNextAction(); // 提示用户进行下一步操作

            if (m_points.Count < 2) // 如果点数不足，等待下一次点击
                return false;

            // 创建并添加线段对象到场景
            ATEC.ATDataCoreNET.ATObjects.ATObject obj = CreateObject(m_points);
            if (obj != null)
                obj.AddToScene(); // 将生成的线加入到场景

            m_points.Clear(); // 清空点集
            m_points.Add(ev.Point); // 将当前终点作为下一条线段的起点

            return CheckSingleShot(); // 如果是一次性命令模式，退出命令
        }

        /// <summary>
        /// 鼠标移动事件处理：动态预览线段效果
        /// </summary>
        /// <param name="ev">鼠标事件参数</param>
        protected override void OnRTUpdateMode(ATEC.ATDataCoreNET.ATBtnEvent ev)
        {
            System.Collections.Generic.List<ATEC.GeometryNET.XPoint3d> tmpPts = new System.Collections.Generic.List<ATEC.GeometryNET.XPoint3d>(m_points);
            tmpPts.Add(ev.Point); // 添加鼠标当前位置点

            ATEC.ATDataCoreNET.ATObjects.ATObject obj = CreateObject(tmpPts); // 创建临时线段
            if (obj == null) return;

            // 动态绘制对象
            ATEC.ATDataCoreNET.RedrawObjs redrawObjs = new ATEC.ATDataCoreNET.RedrawObjs();
            redrawObjs.SetDynamicsViewsFromActiveViewSet(ev.Viewport); // 设定动态视图
            redrawObjs.DrawMode = ATEC.ATDataCoreNET.ATDrawMode.TempDraw; // 设置绘制模式为临时
            redrawObjs.DrawPurpose = ATEC.ATDataCoreNET.DrawPurpose.RTUpdate; // 设置绘制目标为实时更新
            redrawObjs.DoRedraw(obj); // 执行动态绘制
        }

        /// <summary>
        /// 鼠标右键事件处理：结束当前连续线段绘制
        /// </summary>
        /// <param name="ev">鼠标事件参数</param>
        protected override bool OnResetButton(ATEC.ATDataCoreNET.ATBtnEvent ev)
        {
            OnRestartCmd(); // 重启命令以便进行下一次绘制
            return true; // 返回成功状态
        }

        /// <summary>
        /// 重启命令实例
        /// </summary>
        protected override void OnRestartCmd()
        {
            InstallNewInstance(m_CmdName, m_CmdPrompt); // 重新安装命令实例
        }

        /// <summary>
        /// 创建线段对象
        /// </summary>
        /// <param name="points">定义线段的点集合</param>
        /// <returns>生成的线段对象</returns>
        ATEC.ATDataCoreNET.ATObjects.ATObject CreateObject(System.Collections.Generic.List<ATEC.GeometryNET.XPoint3d> points)
        {
            if (2 != points.Count) // 确保点数为2才能创建线段
                return null;

            // 获取当前活动场景
            ATEC.ATDataCoreNET.ATScene scen = SSDPNET.ScenesFunctions.SceneRefGetActive().AsATScenes();

            // 创建直线几何对象
            ATEC.GeometryNET.BasicCurve ln = ATEC.GeometryNET.BasicCurve.CreateLine(new ATEC.GeometryNET.XSegment3d(points[0], points[points.Count - 1]));

            // 将几何对象转换为图形对象
            return ATEC.ATDataCoreNET.ATObjects.GeometryToATObject.ToObject(scen, ln, null);
        }

        /// <summary>
        /// 设置并提示用户下一步操作
        /// </summary>
        void SetupAndPromptForNextAction()
        {
            // 输出提示信息
            string msgStr = (1 == m_points.Count) ? "输入下一个点" : "输入下一个点或右键确认";
            ATEC.ATDataCoreNET.ATNotificationMGR.OutputPrompt(msgStr);

            if (2 != m_points.Count) // 如果点数不足，直接返回
                return;

            // 更新精确捕捉坐标轴
            ATEC.GeometryNET.XVector3d xVec = new ATEC.GeometryNET.XVector3d(m_points[0], m_points[m_points.Count - 1]);
            xVec.NormalizeInPlace(); // 归一化向量
            ATEC.ATDataCoreNET.LimitDesign.SetContext(ATEC.ATDataCoreNET.LimitDesignFlags.SetXAxis, ATEC.GeometryNET.XPoint3d.Zero, xVec); // 设置捕捉上下文
        }

        /// <summary>
        /// 安装新的命令实例
        /// </summary>
        /// <param name="cmdName">命令名称</param>
        /// <param name="toolPrompt">命令提示信息</param>
        public static void InstallNewInstance(string cmdName, string toolPrompt)
        {
            XCmd_CreateLine tool = new XCmd_CreateLine(cmdName, toolPrompt); // 创建新命令实例
            tool.InstallCmd(); // 安装命令
        }
    }


    /// <summary>
    /// 功能：修改直线或线串的命令工具
    /// 描述：此工具允许用户通过鼠标选择和修改图形平台中的线段或线串对象，
    ///      包括调整线段端点或线串的夹点位置。
    /// 类说明：
    /// - 继承自 `ATObjectSetCmd`，实现选择和修改几何对象的命令工具。
    /// - 鼠标交互：支持用户选择线段或线串，并通过鼠标拖动修改其形状。
    /// - 动态特性：根据用户选择的点更新几何对象，并将结果反映到场景中。
    /// 关键类及功能：
    /// - **CurveSet**: 表示曲线集合，包含一组几何曲线对象。
    /// - **BasicCurve**: 提供基本几何曲线类型，如 Line 和 LineString。
    /// - **ATObject**: 表示图形平台中的可视化对象，用于存储和更新几何数据。
    /// 注意事项：
    /// - 只接受 Line 或 LineString 类型的曲线对象。
    /// - 命令支持通过鼠标点调整最近的夹点位置。
    /// - 动态修改的曲线需重新生成并替换场景中的原始对象。
    /// </summary>
    public class XCmd_ModifyLineOrLineString : ATEC.ATDataCoreNET.ATObjectSetCmd
    {
        ATEC.GeometryNET.XPoint3d m_evPoint = ATEC.GeometryNET.XPoint3d.Zero; // 鼠标事件点
        string m_cmdName = ""; // 命令名称

        /// <summary>
        /// 构造函数：初始化命令名称
        /// </summary>
        /// <param name="cmdName">命令名称</param>
        XCmd_ModifyLineOrLineString(string cmdName) : base()
        {
            m_cmdName = cmdName; // 保存命令名称
        }

        /// <summary>
        /// 指定是否允许拾取组对象
        /// </summary>
        /// <returns>是否允许拾取组对象</returns>
        protected override bool DoGroups()
        {
            return false; // 禁止拾取图形命名组成员
        }

        /// <summary>
        /// 决定是否允许附加选择
        /// </summary>
        /// <param name="ev">鼠标事件参数</param>
        /// <returns>是否允许附加选择</returns>
        protected override bool WantAdditionalLocate(ATBtnEvent ev)
        {
            return ObjectSelectSet.GetCount() != 2; // 仅允许选中两个对象
        }

        /// <summary>
        /// 鼠标左键事件处理：执行修改操作
        /// </summary>
        /// <param name="ev">鼠标事件参数</param>
        protected override bool OnDataButton(ATBtnEvent ev)
        {
            if (ObjectSelectSet.GetCount() == 2) // 当选中两个对象时
            {
                for (uint i = 0; i < ObjectSelectSet.GetCount(); ++i)
                {
                    ATObject atTmp = ObjectSelectSet.GetEntry(i); // 获取当前选择的对象
                                                                  // 实现用户定义的功能
                }
                MessageBox.Show("Complete"); // 显示完成提示
                ExitCmd(); // 退出命令
                return true;
            }
            return base.OnDataButton(ev); // 否则调用基类行为
        }

        /// <summary>
        /// 不允许命令启动时使用选择集
        /// </summary>
        /// <returns>选择集使用方式</returns>
        protected override ATEC.ATDataCoreNET.ATObjectSetCmd.UsesSelection AllowSelection()
        {
            return ATEC.ATDataCoreNET.ATObjectSetCmd.UsesSelection.None; // 不使用启动时选择集
        }

        /// <summary>
        /// 处理定位命中的对象，并验证是否可接受
        /// </summary>
        /// <param name="path">命中路径</param>
        /// <param name="cantAcceptReason">无法接受的原因</param>
        /// <returns>是否接受命中的对象</returns>
        protected override bool OnPostLocate(ATEC.ATDataCoreNET.HitPath path, out string cantAcceptReason)
        {
            if (!base.OnPostLocate(path, out cantAcceptReason)) // 调用基类的定位检查
                return false;

            ATEC.ATDataCoreNET.ATObjects.ATObject obj = path.GetHeadObject(); // 获取定位对象
            ATEC.GeometryNET.CurveSet curve = ATEC.ATDataCoreNET.ATObjects.CurvePathQuery.ObjectToCurveSet(obj); // 转换为曲线集

            // 验证是否为 Line 或 LineString
            if (curve == null || !SSDPNET.Geom.CurveSetExtension.IsOpenPath(curve))
                return false;

            if (curve.Count == 1) // 单曲线的情况
            {
                ATEC.GeometryNET.BasicCurve.BasicCurveType t = curve.GetAt(0).GetBasicCurveType();
                switch (t)
                {
                    case ATEC.GeometryNET.BasicCurve.BasicCurveType.Line: // 线段
                    case ATEC.GeometryNET.BasicCurve.BasicCurveType.LineString: // 线串
                        return true;
                    default:
                        return false;
                }
            }
            else
            {
                return false; // 多曲线的情况不支持
            }
        }

        /// <summary>
        /// 为修改操作准备数据
        /// </summary>
        /// <param name="ev">鼠标事件参数</param>
        /// <param name="isRTUpdate">是否为实时更新模式</param>
        /// <returns>是否成功准备数据</returns>
        protected override bool SetupForModify(ATEC.ATDataCoreNET.ATBtnEvent ev, bool isRTUpdate)
        {
            m_evPoint = ev.Point; // 保存鼠标事件点
            return true; // 返回成功
        }

        /// <summary>
        /// 修改图形对象
        /// </summary>
        /// <param name="obj">待修改的对象</param>
        /// <returns>操作状态</returns>
        public override ATEC.ATDataCoreNET.StatusInt OnObjectModify(ATEC.ATDataCoreNET.ATObjects.ATObject obj)
        {
            // 注意：OnPostLocate 已经验证过对象的类型
            ATEC.GeometryNET.CurveSet curve = ATEC.ATDataCoreNET.ATObjects.CurvePathQuery.ObjectToCurveSet(obj);

            ATEC.GeometryNET.XPoint3d locatePoint; // 鼠标定位点
            GetAnchorPoint(out locatePoint); // 获取锚点

            // 找到曲线上的最近点
            ATEC.GeometryNET.CurveDescInfo location = curve.ClosestPointBounded(locatePoint);
            if (location == null)
                return ATEC.ATDataCoreNET.StatusInt.Error;

            // 查找最近的夹点
            int closeVertex = GetCloseVertex(curve, location);
            ATEC.GeometryNET.BasicCurve primitive = curve.GetAt(0); // 获取基本曲线

            // 根据曲线类型更新夹点位置
            switch (primitive.GetBasicCurveType())
            {
                case ATEC.GeometryNET.BasicCurve.BasicCurveType.Line: // 直线段
                    {
                        ATEC.GeometryNET.XSegment3d segment;
                        primitive.TryGetLine(out segment);
                        if (closeVertex == 0)
                            segment.StartPoint = m_evPoint; // 更新起点
                        else
                            segment.EndPoint = m_evPoint; // 更新终点
                        primitive = ATEC.GeometryNET.BasicCurve.CreateLine(segment); // 更新曲线
                        break;
                    }
                case ATEC.GeometryNET.BasicCurve.BasicCurveType.LineString: // 线串
                    {
                        System.Collections.Generic.List<ATEC.GeometryNET.XPoint3d> points = new System.Collections.Generic.List<ATEC.GeometryNET.XPoint3d>();
                        primitive.TryGetLineString(points);
                        points[closeVertex] = m_evPoint; // 更新最近的夹点位置
                        primitive = ATEC.GeometryNET.BasicCurve.CreateLineString(points); // 更新线串
                        break;
                    }
            }

            // 生成新的曲线集并替换原始对象
            ATEC.GeometryNET.CurveSet newCrv = new ATEC.GeometryNET.CurveSet(curve.GetBoundaryType());
            for (int i = 0; i < curve.Count; ++i)
            {
                if (i == 0)
                    newCrv.Add(primitive); // 添加更新后的曲线
                else
                    newCrv.Add(curve.GetAt(i).Clone()); // 添加其他曲线的克隆
            }

            // 替换场景中的原始对象
            ATObject newObject = ATEC.ATDataCoreNET.ATObjects.GeometryToATObject.ToObject(obj.ATScene, newCrv, obj);
            obj.CopyDataFrom(newObject);
            return ATEC.ATDataCoreNET.StatusInt.Success; // 返回成功状态
        }

        /// <summary>
        /// 重启命令实例
        /// </summary>
        protected override void OnRestartCmd()
        {
            InstallNewInstance(m_cmdName); // 安装新命令实例
        }

        /// <summary>
        /// 安装并启动新的命令实例
        /// </summary>
        /// <param name="cmdName">命令名称</param>
        public static void InstallNewInstance(string cmdName)
        {
            XCmd_ModifyLineOrLineString tool = new XCmd_ModifyLineOrLineString(cmdName); // 创建新实例
            tool.InstallCmd(); // 安装并启动命令
        }

        /// <summary>
        /// 获取离鼠标最近的夹点
        /// </summary>
        /// <param name="crv">曲线集</param>
        /// <param name="location">鼠标定位点信息</param>
        /// <returns>最近夹点索引</returns>
        int GetCloseVertex(ATEC.GeometryNET.CurveSet crv, ATEC.GeometryNET.CurveDescInfo location)
        {
            int nSegments = 1; // 默认线段数为 1（非 LineString）
            if (ATEC.GeometryNET.BasicCurve.BasicCurveType.LineString == crv.GetAt(0).GetBasicCurveType())
            {
                int nCount = 1;
                crv.GetAt(0).TryGetLineStringPointCount(out nCount); // 获取线串点数
                nSegments = ((nCount < 3) ? 1 : (nCount - 1)); // 计算线段数
            }

            double uSegRange = (1.0 / nSegments); // 每段的范围
            int closeVertex = (int)((location.Fraction + (uSegRange * .5)) / uSegRange); // 计算最近夹点
            return closeVertex;
        }
    }

    public class UsingCmdMethods
    {
        public static void CmdCreate()
        {
            XCmd_CreateLine.InstallNewInstance("创建线示例", "输入第一个点");
        }

        public static void CmdModify()
        {
            XCmd_ModifyLineOrLineString.InstallNewInstance("修改元素示例");
        }
    }
}